#include <stdio.h>

#include "bmpformat.h"
#include <nds/arm9/video.h>

// hack to declare utility function
void print_hexbytes(unsigned char *byte_pointer, unsigned char length);

//---------------------------------------------------------------------------------
void print_BmpInfo(BmpFile *bmp)
{
	// print header bytes to screen
	print_hexbytes((unsigned char *)&(bmp->header), 14); 
	print_hexbytes((unsigned char *)&(bmp->info), bmp->info.headerSize); 

	printf("\n%c%c:", bmp->header.signature[0], bmp->header.signature[1]);
	printf("%ix%i", bmp->info.width, bmp->info.height);
	printf(" %ibpp\n", bmp->info.bitDepth);
}

//---------------------------------------------------------------------------------
unsigned char validBitDepth(BmpFile *bmp)
{
	if (bmp->info.bitDepth == 32)	return 1;
	if (bmp->info.bitDepth == 24)	return 1;
	if (bmp->info.bitDepth ==  8)	return 1;
	if (bmp->info.bitDepth ==  4)   return 1;

	return 0;
}

//---------------------------------------------------------------------------------
unsigned char validateBmp(BmpFile *bmp)
{
	unsigned char returnval = 1;

	// warn for unhandled BMP format flavours
	if (bmp->info.compression != 0) 
	{
		printf("\nunexpected parameters! :\n");
		printf("\nCOMPRESSION : %i", bmp->info.compression);
		returnval = 0;
	}

	if (!validBitDepth(bmp))
	{
		printf("\nunexpected parameter! : ");
		printf("BPP: %i", bmp->info.bitDepth);
		returnval = 0;
	}

	return returnval;
}


//---------------------------------------------------------------------------------
void displayBmp(BmpFile *bmp, u16 *video_buffer)
{
	unsigned int ix, iy;
	unsigned int next_row, row_width;
	unsigned short file_pixel_size;
	uint8_t *next_pixel;
	uint8_t palette_index;
	Rgb *file_pixel;
	u16 screen_pixel;

	file_pixel_size = bmp->info.bitDepth/8; // bytes per pixel

	row_width = (bmp->info.width * bmp->info.bitDepth) / 8; // integer rounding error if bitDepth < 8
	row_width = (row_width+3) & (~0x03) ; // round up to 4 byte boundary

	//copy the image
	for(iy = 0; iy < bmp->info.height; iy++)
	{
		next_row = (bmp->info.height - 1 - iy);
		next_pixel  = ((uint8_t *)(bmp)) + bmp->header.offset;
		next_pixel += (next_row * row_width);

		if ((bmp->info.bitDepth == 32) || (bmp->info.bitDepth == 24))
		{
			for(ix = 0; ix < bmp->info.width; ix++)
			{
				file_pixel = (Rgb *) next_pixel;
				screen_pixel = RGB15(file_pixel->red >> 3, file_pixel->green >> 3, file_pixel->blue >> 3);
				video_buffer[iy * SCREEN_WIDTH + ix] = screen_pixel | BIT(15);
				next_pixel += file_pixel_size;
			}
		}
		else if (bmp->info.bitDepth == 8)
		{
			for(ix = 0; ix < bmp->info.width; ix++)
			{
				// next_pixel points to index of color in palette, 
				// file_pixel is address of that color in palette
				palette_index = *next_pixel;
				file_pixel = (Rgb *) &(bmp->palette[palette_index]); 
				screen_pixel = RGB15(file_pixel->red >> 3, file_pixel->green >> 3, file_pixel->blue >> 3);
				video_buffer[iy * SCREEN_WIDTH + ix] = screen_pixel | BIT(15);
				next_pixel += file_pixel_size;
			}
		}
		else if (bmp->info.bitDepth == 4)
		{
			//creates two output pixels for each byte input, so count by 2
			for(ix = 0; ix < bmp->info.width; ix+=2)
			{
				// next_pixel points to a byte containing two indices,
				//     each index refers to a color in the palette. 
				// file_pixel is address of that color in palette
				palette_index = ((*next_pixel) >> 4) & 0x0F;	// 
				file_pixel = (Rgb *) &(bmp->palette[palette_index]); 
				screen_pixel = RGB15(file_pixel->red >> 3, file_pixel->green >> 3, file_pixel->blue >> 3);
				video_buffer[iy * SCREEN_WIDTH + ix + 0] = screen_pixel | BIT(15);

				palette_index = ((*next_pixel) >> 0) & 0x0F;
				file_pixel = (Rgb *) &(bmp->palette[palette_index]); 
				screen_pixel = RGB15(file_pixel->red >> 3, file_pixel->green >> 3, file_pixel->blue >> 3);
				video_buffer[iy * SCREEN_WIDTH + ix + 1] = screen_pixel | BIT(15);

				// file_pixel_size of half rounds to zero, so substituting 1 as we processed 2 half bytes
				next_pixel += 1; 
			}
		}
	}
}

//---------------------------------------------------------------------------------
void uploadPalette(BmpFile *bmp, u16 *screen_palette)
{
	unsigned int i, colors;
	Rgb *file_color;
	u16 screen_color;

	colors = bmp->info.numColors;
	if (bmp->info.importantColors != 0) colors = bmp->info.importantColors;

	for (i = 0; i < colors ; i++)
	{
		file_color = (Rgb *) &(bmp->palette[i]); 
		screen_color = RGB15(file_color->red >> 3, file_color->green >> 3, file_color->blue >> 3);
		screen_palette[i] = screen_color;
	}
}

//---------------------------------------------------------------------------------
void displayIndexedBmp(BmpFile *bmp, u16 *video_buffer)
{
	unsigned int ix, iy;
	unsigned int next_row, row_width, vbuf_screenwidth;
	unsigned short file_pixel_size;
	uint8_t *next_pixel;
	uint8_t palette_index;
	u16 vbuf_element;

	file_pixel_size = bmp->info.bitDepth/8; // bytes per pixel

	row_width = (bmp->info.width * bmp->info.bitDepth) / 8; // integer rounding error if bitDepth < 8
	row_width = (row_width+3) & (~0x03) ; // round up to 4 byte boundary

	vbuf_screenwidth = SCREEN_WIDTH/2; 

	//copy the image
	for(iy = 0; iy < bmp->info.height; iy++)
	{
		next_row = (bmp->info.height - 1 - iy);
		next_pixel  = ((uint8_t *)(bmp)) + bmp->header.offset;
		next_pixel += (next_row * row_width);

		// removed no such thing as 24bit & 32bit paletted bmps
		if (bmp->info.bitDepth == 8)
		{
			for(ix = 0; ix < bmp->info.width/2; ix++)
			{
				// next_pixel points to index of color in palette, 
				// file_pixel is address of that color in palette
				palette_index = *next_pixel;
				vbuf_element  = palette_index;
				vbuf_element<<= 8; 
				next_pixel   += file_pixel_size;

				palette_index = *next_pixel;
				vbuf_element |= palette_index;
				next_pixel   += file_pixel_size;

				video_buffer[iy * vbuf_screenwidth + ix] = vbuf_element;
			}
		}
		else if (bmp->info.bitDepth == 4)
		{
			//processes two pixels each iteration
			for(ix = 0; ix < bmp->info.width/2; ix++)
			{
				// next_pixel points to a byte containing two indices,
				//     each index refers to a color in the palette. 
				// palette_index is offset of that color in palette
				palette_index = ((*next_pixel) >> 0) & 0x0F;
				vbuf_element  = palette_index;
				vbuf_element<<= 8; 

				palette_index = ((*next_pixel) >> 4) & 0x0F;
				vbuf_element |= palette_index;
				video_buffer[iy * vbuf_screenwidth + ix] = vbuf_element;

				// file_pixel_size is half, but that rounds to zero, 
				// so substituting 1 here as we processed 2 half bytes
				next_pixel += 1; 
			}
		}
	}
}

//---------------------------------------------------------------------------------
void displayTestPattern(u16 *video_buffer, uint8_t palette_offset, uint8_t colors)
{
	unsigned int ix, iy, vbuf_screenwidth;
	u16 video_pixel;
	uint8_t color_idx;

	// video buffer array of u16, so when using paletted display, each video buffer
	// array element holds two screen pixels
	vbuf_screenwidth = SCREEN_WIDTH/2; 

	for(iy = 0; iy < SCREEN_HEIGHT; iy++)
	{
		for(ix = 0; ix < vbuf_screenwidth; ix++)
		{
			// color_idx changes as we cross the screen,
			// eg ix = vbuf_screenwidth/2, then color_idx = colors/2
			color_idx = palette_offset + (ix*colors/vbuf_screenwidth);
			// collect two input pixels
			video_pixel = (color_idx << 8) | color_idx ;

			// display two output pixels
			video_buffer[(iy*vbuf_screenwidth) + ix] = video_pixel;
		}
	}

}
